This algorithm allows scrolling in any direction. It's a combination of Scroller_XLimited and Scroller_YUnlimited2. Read these documentations first!

The bitmap height, like in Scroller_XLimited algorithm, depends on the width of the map. In most cases the resulting extra height (to be added to the 288 lines (256 + BLOCKHEIGHT * 2)) is very acceptable and therefore not a big disadvantage. If you have for example a map width of 16000 pixels (50 screens) you'll only need 15 extralines. The vertical-fillup-row now consists of 22 blocks (22 * 16 = 352) because the bitmap now needs to be wider to do horizontal scrolling. Analogously the horizontal-fillup-column would have to consist of 18 blocks (18 * 16 = 266) because of the bitmap now needing to be higher to do vertical scrolling. But then the two fillup-areas would overlap, therefore we only use 17 blocks for the horizontal-fillup-column and shift it down by one block:

The single scroll routines are now quite a big more complicated. The reason is that the fillup-column and the fillup-row move during scrolling and that both depend on each other. The moving only happens every BLOCKWIDTH or BLOCKHEIGHT pixels and when this happens to one of the fillup-areas the other fillup-area has to be moved accordingly, too. Now we'll see what to do in each of the scrolling functions:

Scrolling-Right: Startposition (A) is (0,3). Endposition (B) is (16,3). Since the vertical position is not divisible by 16 (BLOCKHEIGHT) the vertical-fillup-row has some 'fillup-blocks' ('down-blocks') in it:

The green blocks represent the position of the horizontal-fillup-column. In this column there are no fillup-blocks, because the moving of the fillup-column only happens at positions that are divisible by 16 (BLOCKWIDTH). So the green blocks can be considered to be part of the normal (grey) blocks.

After the normal action in the scroll routine we are in situation (B). It's obvious that the vertical-fillup-row needs to be corrected so that it is all right again, that is like in (C). The block at the very left and very top (1st arrow) is actually a fillup-block (red) but must become a normal (grey) block. By doing the according blit the vertical-fillup-row will have one fillup-block (red) less. This is not correct so we also blit the now (!) very right fillup-block (2nd arrow). Of course we do this only if there were any fillup-blocks in the vertical-fillup-row (= red blocks), that is when MapPositionY is not divisible by 16 or in other words when the actual vertical step does not equal 0.

Scrolling-Left:: Startposition (A) is (16,3). Endposition (B) is (0,3). Since the vertical position is not divisible by 16 (BLOCKHEIGHT) the vertical-fillup-row has some 'fillup-blocks' ('down-blocks') in it:

Again the vertical-fillup-row after the normal action in the scroll routine is not all right. The block at the very left and the very top (1st arrow) must be reblitted. Since there are some fillup-blocks in the vertical-fillup-row the actually normal (grey) block must be replaced by a fillup-block (red). If there weren't any fillup-blocks in the vertical-fillup-row then we would have to do the reblit anyway, but as a normal (grey) block, because the normal horizontal scrolling functions ignore the area of the vertical-fillup-row. Therefore actually the block at the very left and the very top is block (22,0) of the map. This was correct for MapPositionX 16, but not for MapPositionX 0, where the block must either be block (0,0) of the map, that is a normal block, or block (0,18), that is a fillup-block of the vertical-fillup-row. If the fillup-row contained some fillup-blocks then now we will have blitted a fillup-block (red), with the result that there will be too many fillup-blocks in the fillup-row. In this case, and only (!) in this case we must fix this by overblitting the former (!) very right fillup-block (2nd arrow) with the according normal (grey) map block.

Scrolling-Down: Startposition (A) is (3,0). Endposition (B) is (3,16). Since the horizontal position is not divisible by 16 (BLOCKWIDTH) the horizontal-fillup-column has some 'fillup-blocks' ('right-blocks') in it:

The red blocks represent the position of the vertical-fillup-row. In this row there are no fillup-blocks, because the moving of the fillup-row only happens at positions that are divisible by 16 (BLOCKHEIGHT). So the red blocks can be considered to be part of the normal (grey) blocks.

After the normal action in the scroll routine there is the problem that the position of the horizontal-fillup-column is not what it should be, except if there are no fillup-blocks in the horizontal-fillup-column - in this case we don't have to do anything here, in contrast to the corrections of the vertical-fillup-row during left/right-scrollings where at least one block has to be blitted always. If we are not so lucky two blocks must be blitted. First there is the block at the very left (1st arrow) in the new vertical-fillup-row. which must become a normal-block (grey/red), because the fillup-blocks of the horizontal-fillup-column are not allowed to stay in the vertical-fillup-row. Second there is the now (!) very-bottom fillup-block (2nd arrow). This block is actually a normal (grey) block and must become a fillup-block (green). Here it is very important to know that the 'look' of the horizontal-fillup-column depends on the direction of the last horizontal scrolling, no matter how long ago this happened. If last direction was right it will look like (A), otherwise if last direction was left it will look like (B):

We must blit a fillup-block ('right-block'), that means we have no problems at all if last horizontal direction was right (A). But if last horizontal scroll direction was left (b), then this caused an overblitting of the last planeline of that fillup-block, that stays over the one we want to overblit, that is the former very-bottom fillup-block. Fortunately during left-scrolling we made a backup of this planeline and so we restore it now. Then we can blit our fillup-block - a 'right-block', of course only after having made a backup of the planeline the blit is going to destroy in the normal block staying below. We also have to set the last-scroll-direction-variable to RIGHT, to be able to correctly identify the actual 'state' (A or B) of the fillup-column later and in other places.

Scrolling-Up: Startposition (A) is (3,16). Endposition (B) is (3,0). Since the horizontal position is not divisible by 16 (BLOCKWIDTH) the horizontal-fillup-column has some 'fillup-blocks' ('right-blocks') in it:

Again the horizontal-fillup-column is not all right after the normal action of the scroll routine, if there are fillup-blocks in the column. If there are not, then everything is ok and we don't need to correct anything. Most of the time we won't have so much luck. Then we'll have to blit two blocks. First there is the now (!) very-top staying = first fillup-block (1st arrow) of the horizontal-fillup-column. This block is actually a normal block but must become a fillup-block (green). Second there is the former(!) very-bottom staying fillup-block (2nd arrow), This block must become a normal-block. One more time we must look at the actual 'state' of the fillup-column. If last horizontal scroll direction was left, then everything is okay. But if the direction was right, then this caused an overblitting of the first planeline of that normal-block ,that stays below the one we want to overblit. Fortunately during right-scrolling we made a backup of this planeline and so we restore it now. Then we can blit our block a 'normal/left-block', of course only after having made a backup of the planeline the blit is going to destroy in the fillup-block staying above. Finally we have to set the last-scroll-direction-variable to LEFT, to be able to correctly identify the actual 'state' (A or B) of the fillup-column later and in other places.

What's more to say? The modulo-trick requires a few changes. Because of horizontal scrolling the videochip reads more than 320 pixels per line. Depending on the FETCH Mode, we are talking about 16 (1x), 32 (2x) or 64 (4x) pixels. This must be taken into consideration when calculating the special-modulo. On the other hand it is not necessary to now do the calculation in the UpdateCopperlist() function, despite of the address to which we set the plane pointers in the VIDEOSPLIT rasterline no longer being constant, in contrast to Scroller_YUnlimited2 algorithm. We don't have to do it, because of the offset between old address and new address staying constant. Therefore the special-modulo stays constant, too. But there is something else we must move out of InitCopperlist() and into UpdateCopperlist(), the routines which set the Hiwords. We must do it because of the VIDEOSPLIT plane addresses no longer being constant, as already said. Despite the fact that they are affected only by horizontal scrolling, it's our bitmap, which unfortunately is not aligned correctly, to be able to guarantee that the Hiwords remain constant.

Some comments about the demo source code. Horizontally the first BLOCKWIDTH (16) and vertically the first BLOCKHEIGHT (16) pixels of the map are not visible. Staying at a map position of (mapposx,mapposy) the user will see the following area:

(mapposx + BLOCKWIDTH,mapposy + BLOCKHEIGHT) -
(mapposx + BLOCKWIDTH + SCREENWIDTH - 1,mapposy + BLOCKHEIGHT + SCREENHEIGHT - 1)

If you want to blit something into the bitmap, for example blitter objects (BOBs) then you must do it in the actual visible bitmap area:

(videoposx + BLOCKWIDTH,(videoposy + BLOCKHEIGHT) % BITMAPHEIGHT) -
(videoposx + BLOCKWIDTH + SCREENWIDTH - 1,(videoposy + BLOCKHEIGHT + SCREENHEIGHT - 1) % BITMAPHEIGHT)

As you can imagine when looking at the above calculations it may happen that the end Y coordinate is above the start Y coordinate, that is, it is lower. This is because of the video splitting and the only thing it means is that the area starts at the start coordinate and goes until the end of the bitmap, then it "wraps" to the top of the bitmap where it still goes until the end coordinate. For blitting this means that it is necessary to take a close look at the blit height and the calculated blit destination Y coordinate (blitarea_strty + destinationy):

The start coordinates can be rounded down and the end coordinates can be rounded up to a multiple of BLOCKWIDTH/BLOCKHEIGHT. The resulting blitable area is (SCREENWIDTH + BLOCKWIDTH) x (SCREENHEIGHT + BLOCKHEIGHT) pixels big. In this algorithm as well it may happen that the end Y coordinate is above the start Y coordinate, that is, it is lower. This is because of the video splitting and the only thing it means is, that the area starts at the start coordinate and goes until the end of the bitmap, then it "wraps" to the top of the bitmap where it still goes until the end coordinate. For blitting this means that it is necessary to take a close look at the blit height and the calculated blit destination Y coordinate (blitarea_strty + destinationy):

If BLITDESTINATIONY + BLITHEIGHT <= BITMAPHEIGHT then:
Normally blit at Y position BLITDESTINATIONY
else, if BLITDESTINATIONY >= BITMAPHEIGHT then:
Normally blit at Y position (BLITDESTINATIONY - BITMAPHEIGHT)
else:
Blit the first (BITMAPHEIGHT - BLITDESTINATIONY) pixellines at Y position BLITDESTINATIONY
Blit the last (BLITHEIGHT - (BITMAPHEIGHT - BLITDESTINATIONY) pixellines at Y-Positionen 0